package com.hdu.kdk_feign.utils;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Map;
import java.util.StringJoiner;

import static com.alibaba.fastjson.JSON.toJSONString;
import static java.util.Optional.ofNullable;

@Slf4j
public class HttpUtils {

    private static final String SEPARATOR_1 = "&";
    private static final String SEPARATOR_2 = "?";
    private static final String SEPARATOR_3 = "=";
    private static final String SEPARATOR_4 = "/";

    public static String doGet(String url) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpGet httpGet = new HttpGet(url);
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
            HttpEntity responseEntity = response.getEntity();
            return EntityUtils.toString(responseEntity);
        } catch (ParseException | IOException e) {
            log.error("HttpUtils.doGet() error", e);
            return null;
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                log.error("HttpUtils.doGet() error", e);
            }
        }
    }

    public static String doGet(String url, Object... pathVariables) {
        return doGet(getQueryUrl(url, null, pathVariables));
    }

    public static String doGet(String url, Map<String, Object> queryParam) {
        return doGet(getQueryUrl(url, queryParam, (Object[]) null));
    }

    public static String doGet(String url, Map<String, Object> queryParam, Object... pathVariables) {
        return doGet(
                getQueryUrl(url, queryParam, pathVariables)
        );
    }

    public static String doPost(String url) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            return EntityUtils.toString(responseEntity);
        } catch (ParseException | IOException e) {
            log.error("HttpUtils.doPost() error", e);
            return null;
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                log.error("HttpUtils.doPost() error", e);
            }
        }
    }

    public static String doPost(String url, Map<String, Object> queryParam) {
        return doPost(getQueryUrl(url, queryParam, (Object[]) null));
    }

    public static String doPost(String url, Object... pathVariables) {
        return doPost(getQueryUrl(url, null, pathVariables));
    }

    public static String doPost(String url, Object postReq, Map<String, Object> queryParam, Object... pathVariables) {
        val httpClient = HttpClientBuilder.create().build();
        val httpPost = new HttpPost(
                getQueryUrl(url, queryParam, pathVariables)
        );
        ofNullable(postReq).ifPresent(pr -> {
            val jsonString = toJSONString(pr);
            val entity = new StringEntity(jsonString, "UTF-8");
            httpPost.setEntity(entity);
            httpPost.setHeader("Content-Type", "application/json;charset=utf8");
        });
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            return EntityUtils.toString(responseEntity);
        } catch (ParseException | IOException e) {
            log.error("HttpUtils.doPost() error", e);
            return null;
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                log.error("HttpUtils.doPost() error", e);
            }
        }
    }


    public static String doPost(String url, Object postReq) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        String jsonString = toJSONString(postReq);
        StringEntity entity = new StringEntity(jsonString, "UTF-8");
        httpPost.setEntity(entity);
        httpPost.setHeader("Content-Type", "application/json;charset=utf8");
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            return EntityUtils.toString(responseEntity);
        } catch (ParseException | IOException e) {
            log.error("HttpUtils.doPost() error", e);
            return null;
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                log.error("HttpUtils.doPost() error", e);
            }
        }
    }

    private static String getQueryUrl(String url, Map<String, Object> queryParam, Object... pathVariables) {
        val queryUrl = new StringBuilder(url);
        ofNullable(pathVariables).ifPresent(pvs -> {
            for (Object pv : pvs) {
                queryUrl.append(SEPARATOR_4).append(toPathVariables(pv));
            }
        });
        ofNullable(queryParam).ifPresent(qp -> queryUrl.append(SEPARATOR_2).append(toQueryParams(qp)));
        return queryUrl.toString();
    }

    private static String toQueryParams(Map<String, Object> paramMap) {
        StringJoiner params = new StringJoiner(SEPARATOR_1);
        paramMap.entrySet()
                .stream()
                .map(param -> param.getKey() + SEPARATOR_3 + URLEncoder.encode(param.getValue().toString()))
                .forEach(params::add);
        return params.toString();
    }

    private static String toPathVariables(Object... pathVariables) {
        StringJoiner params = new StringJoiner(SEPARATOR_4);
        Arrays.stream(pathVariables).map(Object::toString).forEach(params::add);
        return params.toString();
    }
}